package project.newCode.surfaces;

import java.awt.Color;

import project.hulpPakket.Color3f;
import project.hulpPakket.Matrix4f;
import project.hulpPakket.Point3f;
import project.hulpPakket.TexCoord2f;
import project.hulpPakket.Vector3f;
import project.newCode.Hitrecord;
import project.newCode.Ray;
import project.newCode.materials.Material;

// NOTE: To ease the storage cost, perhaps a separate TriangleGroup class can be implemented which stores only verteces,..
public class Triangle extends Surface{
	private Point3f [] p;
	private TexCoord2f [] tex;
	private Vector3f [] n;

	public Triangle(Point3f [] p1, TexCoord2f [] t1, Vector3f [] n1, Material m1) {
		super(m1);
		this.p=p1;
		this.tex=t1;
		this.n=n1;
	}
	@Override
	public boolean hit(Ray r, double t0, double t1, Hitrecord rec) {
		float a = p[0].x-p[1].x;
		float b = p[0].y-p[1].y;
		float c = p[0].z-p[1].z;
		float d = p[0].x-p[2].x;
		float e = p[0].y-p[2].y;
		float f = p[0].z-p[2].z;
		float g = r.getDirection().x;
		float h = r.getDirection().y;
		float i = r.getDirection().z;
		float j = p[0].x-r.getOrigin().x;
		float k = p[0].y-r.getOrigin().y;
		float l = p[0].z-r.getOrigin().z;
		float eiminhf = e*i - h*f;
		float gfmindi = g*f - d*i;
		float dhmineg = d*h - e*g;
		float M = a*eiminhf+b*gfmindi+c*dhmineg;
		if(M==0) {
			return false;
		}
		float t = -1*(f*(a*k-j*b)+e*(j*c-a*l)+d*(b*l-k*c))/M;
		if(t < t0 || t > t1) {
			return false;
		}
		float gamma = (i*(a*k-j*b)+h*(j*c-a*l)+g*(b*l-k*c))/M;
		if(gamma < 0 || gamma > 1) {
			return false;
		}
		float beta = (j*eiminhf+k*gfmindi+l*dhmineg)/M;
		if(beta < 0 || beta > (1-gamma)) {
			return false;
		}
		rec.t = t;
		rec.intersection = Point3f.Plus(r.getOrigin(), r.getDirection().multiply(t));
		rec.normal = Vector3f.cross(Point3f.Min(p[1], p[0]), Point3f.Min(p[2],p[0])).normalise();
		Vector3f bary = baryCentrics(rec.intersection);
		rec.normal = Vector3f.Plus(n[0].multiply(bary.x), Vector3f.Plus(n[1].multiply(bary.y),n[2].multiply(bary.z) )).normalise();
		if(tex.length>0) {
			rec.tex = TexCoord2f.bary(tex[0], bary.x, tex[1], bary.y, tex[2], bary.z);
		}
		this.setMaterialProperties(rec);
		return true;
	}

	@Override
	public Box boundingbox() {
		Point3f max = p[0].clone();
		Point3f min = p[0].clone();
		for(int j=0; j<3; j++) {
			if(p[j].x > max.x){ max.x=p[j].x;}
			if(p[j].y > max.y){ max.y=p[j].y;}
			if(p[j].z > max.z){ max.z=p[j].z;}
			if(p[j].x < min.x){ min.x=p[j].x;}
			if(p[j].y < min.y){ min.y=p[j].y;}
			if(p[j].z < min.z){ min.z=p[j].z;}
		}
		return new Box(min,max);
	}


	public Vector3f baryCentrics(Point3f p1) {
		Vector3f n = Vector3f.cross(Point3f.Min(p[1], p[0]), Point3f.Min(p[2],p[0]));
		Vector3f na = Vector3f.cross(Point3f.Min(p[2], p[1]), Point3f.Min(p1,p[1]));
		Vector3f nb = Vector3f.cross(Point3f.Min(p[0], p[2]), Point3f.Min(p1,p[2]));
		Vector3f nc = Vector3f.cross(Point3f.Min(p[1], p[0]), Point3f.Min(p1,p[0]));
		float normn2=(float) n.norm();
		return new Vector3f(Vector3f.Inp(n, na)/normn2,Vector3f.Inp(n, nb)/normn2,Vector3f.Inp(n, nc)/normn2);
	}

	@Override
	public Triangle Transform(Matrix4f t, Matrix4f i) {
		Point3f [] pnew = new Point3f[3];
		Vector3f [] nnew = new Vector3f[3];
		Matrix4f it = i.transpose();
		for(int j=0; j<3; j++) {
			nnew[j] = new Vector3f(Matrix4f.prod3p(it, new Point3f(n[j]))).normalise();
			pnew[j] = Matrix4f.prod3p(t, p[j]);
		}
		return new Triangle(pnew,this.tex,nnew,this.getMaterial());
	}
	@Override
	public Surface surfaceClone() {
		return new Triangle(p.clone(),tex.clone(),n.clone(),this.getMaterial());
	}

}
